package me.w70.bot.methods;

import java.awt.Point;
import java.awt.Rectangle;

import me.w70.bot.Bot;
import me.w70.bot.wrappers.RSInterface;
import me.w70.bot.wrappers.RSInterfaces;
import me.w70.hooks.Interface;
import me.w70.hooks.Tile;

public class Calc {
	private static Rectangle GAMESCREEN = new Rectangle(4, 4, 512, 334);

	private static final int[] CURVESIN = new int[2048];

	private static final int[] CURVECOS = new int[2048];

	// calculate the curves
	static {
		for (int i = 0; i < 2048; i++) {
			CURVESIN[i] = (int) (65536.0 * Math.sin(i * 0.0030679615));
			CURVECOS[i] = (int) (65536.0 * Math.cos(i * 0.0030679615));
		}
	}

	public static double calculateDistance(final Tile curr, final Tile dest) {
		return Math.sqrt((curr.getX() - dest.getX())
				* (curr.getX() - dest.getX()) + (curr.getY() - dest.getY())
				* (curr.getY() - dest.getY()));
	}
	
	public static int getRealDistanceTo(final int startX, final int startY,
			final int destX, final int destY, final boolean isObject) {
		final int[][] via = new int[104][104];
		final int[][] cost = new int[104][104];
		final int[] tileQueueX = new int[4000];
		final int[] tileQueueY = new int[4000];

		for (int xx = 0; xx < 104; xx++) {
			for (int yy = 0; yy < 104; yy++) {
				via[xx][yy] = 0;
				cost[xx][yy] = 99999999;
			}
		}

		int curX = startX;
		int curY = startY;
		via[startX][startY] = 99;
		cost[startX][startY] = 0;
		int head = 0;
		int tail = 0;
		tileQueueX[head] = startX;
		tileQueueY[head++] = startY;
		boolean foundPath = false;
		final int pathLength = tileQueueX.length;
		final int blocks[][] = Bot.methods.getClient().getGroundDataArray()[Bot.methods
				.getClient().getPlane()].getBlocks();
		while (tail != head) {
			curX = tileQueueX[tail];
			curY = tileQueueY[tail];

			if (!isObject && curX == destX && curY == destY) {
				foundPath = true;
				break;
			} else if (isObject) {
				if (curX == destX && curY == destY + 1 || curX == destX
						&& curY == destY - 1 || curX == destX + 1
						&& curY == destY || curX == destX - 1 && curY == destY) {
					foundPath = true;
					break;
				}
			}
			tail = (tail + 1) % pathLength;

			// Big and ugly block of code
			final int thisCost = cost[curX][curY] + 1;
			if (curY > 0 && via[curX][curY - 1] == 0
					&& (blocks[curX][curY - 1] & 0x1280102) == 0) {
				tileQueueX[head] = curX;
				tileQueueY[head] = curY - 1;
				head = (head + 1) % pathLength;
				via[curX][curY - 1] = 1;
				cost[curX][curY - 1] = thisCost;
			}
			if (curX > 0 && via[curX - 1][curY] == 0
					&& (blocks[curX - 1][curY] & 0x1280108) == 0) {
				tileQueueX[head] = curX - 1;
				tileQueueY[head] = curY;
				head = (head + 1) % pathLength;
				via[curX - 1][curY] = 2;
				cost[curX - 1][curY] = thisCost;
			}
			if (curY < 104 - 1 && via[curX][curY + 1] == 0
					&& (blocks[curX][curY + 1] & 0x1280120) == 0) {
				tileQueueX[head] = curX;
				tileQueueY[head] = curY + 1;
				head = (head + 1) % pathLength;
				via[curX][curY + 1] = 4;
				cost[curX][curY + 1] = thisCost;
			}
			if (curX < 104 - 1 && via[curX + 1][curY] == 0
					&& (blocks[curX + 1][curY] & 0x1280180) == 0) {
				tileQueueX[head] = curX + 1;
				tileQueueY[head] = curY;
				head = (head + 1) % pathLength;
				via[curX + 1][curY] = 8;
				cost[curX + 1][curY] = thisCost;
			}
			if (curX > 0 && curY > 0 && via[curX - 1][curY - 1] == 0
					&& (blocks[curX - 1][curY - 1] & 0x128010e) == 0
					&& (blocks[curX - 1][curY] & 0x1280108) == 0
					&& (blocks[curX][curY - 1] & 0x1280102) == 0) {
				tileQueueX[head] = curX - 1;
				tileQueueY[head] = curY - 1;
				head = (head + 1) % pathLength;
				via[curX - 1][curY - 1] = 3;
				cost[curX - 1][curY - 1] = thisCost;
			}
			if (curX > 0 && curY < 104 - 1 && via[curX - 1][curY + 1] == 0
					&& (blocks[curX - 1][curY + 1] & 0x1280138) == 0
					&& (blocks[curX - 1][curY] & 0x1280108) == 0
					&& (blocks[curX][curY + 1] & 0x1280120) == 0) {
				tileQueueX[head] = curX - 1;
				tileQueueY[head] = curY + 1;
				head = (head + 1) % pathLength;
				via[curX - 1][curY + 1] = 6;
				cost[curX - 1][curY + 1] = thisCost;
			}
			if (curX < 104 - 1 && curY > 0 && via[curX + 1][curY - 1] == 0
					&& (blocks[curX + 1][curY - 1] & 0x1280183) == 0
					&& (blocks[curX + 1][curY] & 0x1280180) == 0
					&& (blocks[curX][curY - 1] & 0x1280102) == 0) {
				tileQueueX[head] = curX + 1;
				tileQueueY[head] = curY - 1;
				head = (head + 1) % pathLength;
				via[curX + 1][curY - 1] = 9;
				cost[curX + 1][curY - 1] = thisCost;
			}
			if (curX < 104 - 1 && curY < 104 - 1
					&& via[curX + 1][curY + 1] == 0
					&& (blocks[curX + 1][curY + 1] & 0x12801e0) == 0
					&& (blocks[curX + 1][curY] & 0x1280180) == 0
					&& (blocks[curX][curY + 1] & 0x1280120) == 0) {
				tileQueueX[head] = curX + 1;
				tileQueueY[head] = curY + 1;
				head = (head + 1) % pathLength;
				via[curX + 1][curY + 1] = 12;
				cost[curX + 1][curY + 1] = thisCost;
			}
		}
		if (foundPath) {
			return cost[curX][curY];
		}
		return -1;
	}

	public static int tileHeight(final int x, final int y) {
		final int[][][] ground = Bot.methods.getClient().getGroundHeights();
		int zidx = Bot.methods.getClient().getPlane();
		final int x1 = x >> 7;
		final int y1 = y >> 7;
		final int x2 = x & 127;
		final int y2 = y & 127;
		// System.out.println((0x7f);

		if (x1 < 0 || y1 < 0 || x1 > 103 || y1 > 103) {
			return 0;
		}

		if (zidx < 3
				&& (2 & Bot.methods.getClient().getGroundSettings()[1][x1][y1]) == 2) {
			zidx++;
		}

		final int i = ground[zidx][x1 + 1][y1] * x2 + ground[zidx][x1][y1]
				* (128 - x2) >> 7;
		final int j = ground[zidx][x1 + 1][y1 + 1] * x2
				+ ground[zidx][x1][y1 + 1] * (128 - x2) >> 7;

		return j * y2 + (128 - y2) * i >> 7;
	}

	public static Point worldToScreen(double X, double Y, final int height) {

		X += 5;
		Y -= 11;
		if (X < 128 || Y < 128 || X > 13056 || Y > 13056
				|| Bot.methods.getClient().getGroundHeights() == null) {
			return new Point(-1, -1);
		}
		int tileCalculation = Calc.tileHeight((int) X, (int) Y) - height;
		X -= Bot.methods.getClient().getCameraX();
		tileCalculation -= Bot.methods.getClient().getCameraZ();
		final int curvexsin = Calc.CURVESIN[Bot.methods.getClient()
				.getCameraCurveX()];
		final int curvexcos = Calc.CURVECOS[Bot.methods.getClient()
				.getCameraCurveX()];
		Y -= Bot.methods.getClient().getCameraY();
		final int curveysin = Calc.CURVESIN[Bot.methods.getClient()
				.getCameraCurveY()];
		final int curveycos = Calc.CURVECOS[Bot.methods.getClient()
				.getCameraCurveY()];
		int calculation = curvexsin * (int) Y + (int) X * curvexcos >> 16;

		Y = -(curvexsin * (int) X) + (int) Y * curvexcos >> 16;
		X = calculation;
		calculation = curveycos * tileCalculation - curveysin * (int) Y >> 16;
		Y = curveysin * tileCalculation + (int) Y * curveycos >> 16;
		tileCalculation = calculation;
		if (Y < 50) {
			return new Point(-1, -1);
		}

		final int ScreenX = ((int) X << 9) / (int) Y + 256;
		final int ScreenY = (tileCalculation << 9) / (int) Y + 167;

		final Point p = new Point(ScreenX, ScreenY);
		if (Calc.isOnGameScreen(p)) {
			return p;
		}
		return new Point(-1, -1);
	}

	private static boolean isOnGameScreen(Point p) {
		return GAMESCREEN.contains(p);
	}

	public static int random(int min, int max) {
		return ((int) (Math.random() * (max - min))) + min;
	}

	public static boolean onScreen(int x, int y) {
		return x > -1 || y > -1 || x < 757 || y < 504;
	}

	public static boolean onScreen(Point p) {
		return onScreen(p.x, p.y);
	}
	
	public static Point tileToMinimap2(Tile tile)
    {

        int mmHeight = 148;
        int mmWidth = 148;
        int mmMasterX = 516;
        int mmMasterY = 0;
        int mmGetX = 35;
        int mmGetY = 9;

        double x = tile.getX();
        double y = tile.getY();

        x -= Bot.methods.getClient().getBaseX();
        y -= Bot.methods.getClient().getBaseY();

        int calculatedX = ((int)(x * 4 + 2)) - Bot.methods.getClient().getMyPlayer().getPosX() * 4;
        int calculatedY = ((int)(y * 4 + 2)) - Bot.methods.getClient().getMyPlayer().getPosY() * 4;

        int angle = (int)Bot.methods.getClient().getCompassAngle() + Bot.methods.getClient().getMinimapOffsetAngle() & 0x7ff;
        int actDistSq = calculatedX * calculatedX + calculatedY * calculatedY;

        int mmDist = Math.max(mmHeight / 2, mmWidth / 2);
   //     if (mmDist * mmDist >= actDistSq)
   //     {
            int cs = CURVESIN[angle];
            int fact = 256 + Bot.methods.getClient().getMinimapScale();
            cs = 256 * cs / fact;
            int cc = CURVECOS[angle];
            cc = 256 * cc / fact;
            int i_25_ = -(calculatedX * cs) + calculatedY * cc >> 16;
            int i_26_ = calculatedX * cc + calculatedY * cs >> 16;

            int screenx = (mmGetX + mmMasterX) + mmWidth / 2 + i_26_;
            int screeny = -i_25_ + mmHeight / 2 + (mmGetY + mmMasterY);
           
            return new Point(screenx, screeny);
     /*   }
        else
        {
            return new Point(-1,-1);//not on minimap
        }
*/
    }

	/**
	 * Calculates a point minimap
	 * 
	 * @param x
	 * @param y
	 * @return new Point(-1, -1) if not on minimap.
	 */
	public static Point worldToMinimap(double x, double y) {
		/* HACK TODO FIX */
		   int mmHeight = 148;
           int mmWidth = 148;
           int mmMasterX = 516;
           int mmMasterY = 0;
           int mmGetX = 35;
           int mmGetY = 9;
         /*   */
		x -= Bot.methods.getClient().getBaseX();
		y -= Bot.methods.getClient().getBaseY();
		final int calculatedX = (int) (x * 4 + 2)
		- Bot.methods.getClient().getMyPlayer().getPosX() / 32;
		final int calculatedY = (int) (y * 4 + 2)
		- Bot.methods.getClient().getMyPlayer().getPosY() / 32;

		try {
		//	Interface mm = Bot.methods.getClient().getInterfaces()[548][65];

			final int angle = 0x7ff & Bot.methods.getClient().getMinimapOffsetAngle()
			+ (int) Bot.methods.getClient().getCompassAngle();
			final int actDistSq = calculatedX * calculatedX + calculatedY
			* calculatedY;
	
			final int mmDist = Math.max(mmHeight / 2, mmWidth / 2) + 10;
		//	if (mmDist * mmDist >= actDistSq) {
				int cs = Calc.CURVESIN[angle];
				final int fact = 256 + Bot.methods.getClient().getMinimapScale();
				cs = 256 * cs / fact;
				int cc = Calc.CURVECOS[angle];
				cc = 256 * cc / fact;
				final int i_25_ = -(calculatedX * cs) + calculatedY * cc >> 16;
				final int i_26_ = calculatedX * cc + calculatedY * cs >> 16;
	
				final int screenx = mmGetX + mmMasterX + mmWidth / 2
				+ i_26_;
				final int screeny = -i_25_ + mmHeight / 2 + mmGetY
				+ mmMasterY;
	
				// Check if point is within the circle of the minimap instead of the
				// rectangle!
			//	if (Math.max(i_26_, -i_26_) <= mmWidth / 2.0 * .8
			//			&& Math.max(i_25_, -i_25_) <= mmHeight / 2 * .8) {
					return new Point(screenx+2, screeny);
			//	} else {
			//		return new Point(-1, -1);
			//	}
	//		}
		} catch (NullPointerException npe) {
			// return new Point(-1, -1);
		}

		return new Point(-1, -1);// not on minimap
	}
	/**
	 * Calculates a point minimap
	 * 
	 * @param x
	 * @param y
	 * @return new Point(-1, -1) if not on minimap.
	 */
	public static Tile worldToMinimapTile(double x, double y) {
		x -= Bot.methods.getClient().getBaseX();
		y -= Bot.methods.getClient().getBaseY();
		int calculatedX = ((int) (x * 4 + 2))
				- Bot.methods.getClient().getMyPlayer().getPosX() / 32;
		int calculatedY = ((int) (y * 4 + 2))
				- Bot.methods.getClient().getMyPlayer().getPosY() / 32;

		RSInterface mm = RSInterfaces.getInterfaceGroup(548).getInterface(65);

		int angle = 0x7ff & Bot.methods.getClient().getMinimapOffsetAngle()
				+ (int) Bot.methods.getClient().getMinimapOffsetAngle();
		int actDistSq = calculatedX * calculatedX + calculatedY * calculatedY;

		int mmDist = Math.max(mm.getHeight() / 2, mm.getWidth() / 2) + 10;
		if (mmDist * mmDist >= actDistSq) {
			int cs = CURVESIN[angle];
			int fact = 256 + Bot.methods.getClient().getMinimapScale();
			cs = 256 * cs / fact;
			int cc = CURVECOS[angle];
			cc = 256 * cc / fact;
			int i_25_ = -(calculatedX * cs) + calculatedY * cc >> 16;
			int i_26_ = calculatedX * cc + calculatedY * cs >> 16;

			int screenx = (mm.getX()) + mm.getWidth() / 2 + i_26_;
			int screeny = -i_25_ + mm.getHeight() / 2 + (mm.getY());

			// Check if point is within the circel of the minimap instead of the
			// rectangle!
			if (Math.max(i_26_, -i_26_) <= (mm.getWidth() / 2.0) * .8
					&& Math.max(i_25_, -i_25_) <= (mm.getHeight() / 2) * .8)
				return new Tile(screenx, screeny);
			else
				return new Tile(-1, -1);
		}
		return new Tile(-1, -1);// not on minimap
	}

	public static Point tileToScreen(final int x, final int y, final double dX,
			final double dY, final int height) {
		return Calc.worldToScreen(
				(x - Bot.methods.getClient().getBaseX() + dX) * 128, (y
						- Bot.methods.getClient().getBaseY() + dY) * 128,
				height);
	}

	public static Point tileToScreen(final int x, final int y, final int height) {
		return Calc.tileToScreen(x, y, 0.5, 0.5, height);
	}

	public static Point tileToScreen(final Tile t) {
		return Calc.tileToScreen(t.getX(), t.getY(), 0);
	}

	public static Point tileToScreen(final Tile t, final double dX,
			final double dY, final int height) {
		return Calc.tileToScreen(t.getX(), t.getY(), dX, dY, height);
	}

	public static Point tileToScreen(final Tile t, final int h) {
		return Calc.tileToScreen(t.getX(), t.getY(), h);
	}

	public static Point tileToMinimap(Tile tile) {
		return worldToMinimap(tile.getX(), tile.getY());
	}

	public static Tile tileToMinimapTile(Tile tile) {
		return worldToMinimapTile(tile.getX(), tile.getY());
	}
}